/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.paoding.rose;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import net.paoding.rose.web.Invocation;
import net.paoding.rose.web.impl.context.ContextLoader;
import net.paoding.rose.web.impl.validation.ParameterBindingResult;
import net.paoding.rose.web.var.CookieHelper;
import net.paoding.rose.web.var.Flash;
import net.paoding.rose.web.var.Model;
import net.paoding.rose.web.var.PrivateVar;

import org.springframework.util.Assert;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
 * {@link Rc}用于在基于Paoding Rose框架的系统中使用。开发者通过{@link Rc}
 * 提供的静态方法可以访问到全局性的对象(比如ServletContext、WebApplicationContext对象)以及和当前request-
 * scope相关的对象(比如请求、响应、Cookie、参数等)。
 * <p>
 * 请参考具体的API说明
 * 
 * @author qieqie.wang@paoding.net
 * 
 */
public class Rc {

	/**
	 * 获取这个webapp的ServletContext
	 * 
	 * @return
	 */
	public static ServletContext getServletContext() {
		return PrivateVar.servletContext();
	}

	/**
	 * 获取指定配置文件装载进来的上下文环境(默认是WEB-INF以及类路径下的applicationContext*.xml,*代表任意字符包括空串)
	 * 
	 * @return
	 */
	public static XmlWebApplicationContext getRootWebApplicationContext() {
		return ContextLoader.getRootWebApplicationContext(PrivateVar.servletContext());
	}

	/**
	 * 获取某个module特有的上下文环境
	 * 
	 * @return
	 */
	public static XmlWebApplicationContext getWebApplicationContext(String modulePath) {
		return (XmlWebApplicationContext) getServletContext().getAttribute(
				WebApplicationContext.class.getName() + ".module_" + modulePath);
	}

	/**
	 * 获取当前module特有的上下文环境
	 * 
	 * @return
	 */
	public static XmlWebApplicationContext getWebApplicationContext() {
		return getWebApplicationContext(PrivateVar.getInvocation().getModule().getPath());
	}

	/**
	 * 在控制器中调用此方法，返回当前的HTTP请求对象
	 * <p>
	 * 注意：如果要使用转发功能，请尽量在控制器的action方法中使用return字符串的形式体现。<br>
	 * 具体情况请参考《Reference》的《控制器返回》章节说明
	 * 
	 * @return
	 */
	public static HttpServletRequest request() {
		return PrivateVar.request();
	}

	/**
	 * 在控制器中调用此方法，返回当前的HTTP响应对象
	 * 
	 * @return
	 */
	public static HttpServletResponse response() {
		return invocation().getResponse();
	}

	/**
	 * 在控制器中调用此方法，返回当前的HttpSession,如果之前没有HttpSession，按照Servlet API说明将自动创建
	 * 
	 * @return
	 */
	public static HttpSession session() {
		return request().getSession();
	}

	/**
	 * 在控制器中调用此方法，返回当前的HttpSession，如果之前没有HttpSession，则按照create参数指示是否创建
	 * 
	 * @param create
	 * @return
	 */
	public static HttpSession session(boolean create) {
		return request().getSession(create);
	}

	/**
	 * 返回指定名称的cookie值，如果没有该cookie则返回null
	 * 
	 * @param name
	 * @return
	 */
	public static String cookie(String name) {
		return cookie().get(name) == null ? null : cookie().get(name).getValue();
	}

	/**
	 * 返回指定名称的cookie数组对象，如果没有返回的数组长度为0
	 * 
	 * @param name
	 * @return
	 */
	public static Cookie[] cookies(String name) {
		return cookie().find(name);
	}

	/**
	 * 增加一个Cookie
	 * 
	 * @param name
	 * @param value
	 */
	public static void addCookie(String name, String value) {
		cookie().add(name, value);
	}

	/**
	 * 增加一个Cookie
	 * 
	 * @param cookie
	 */
	public static void addCookie(Cookie cookie) {
		cookie().add(cookie);
	}

	/**
	 * 返回Cookie操作帮助对象。
	 * <p>
	 * 一般，通过 {@link #cookie(String)} {@link #cookies(String)}以及
	 * {@link #addCookie(String, String)} 、 {@link #addCookie(Cookie)} 这些API处理即可
	 * 
	 * @return
	 */
	public static CookieHelper cookie() {
		return new CookieHelper();
	}

	/**
	 * 将对象、数组对象、集合对象加入到MVC的M中。(具体请看靠 {@link Model#add(Object)})
	 * 
	 * @see Model#add(Object)
	 */
	public static Model addModel(Object value) {
		return model().add(value);
	}

	/**
	 * 将对象、数组对象、集合对象加入到MVC的M中。(具体请看靠 {@link Model#add(String, Object)})
	 * 
	 * @see Model#add(String, Object)
	 */
	public static Model addModel(String name, Object value) {
		return model().add(name, value);
	}

	/**
	 * 在控制器中调用此方法，获取当前的MVC架构中的模型容器，然后通过调用model的add方法将对象传送到视图View中
	 * <p>
	 * 一般情况直接使用 {@link #addModel(Object)}或 {@link #addModel(String, Object)}即可
	 * 
	 * @return
	 */
	public static Model model() {
		return invocation().getModel();
	}

	/**
	 * 获取Flash对象，支持跨请求的信息传递(比如在redirect中)
	 * 
	 * @return
	 */
	public static Flash flash() {
		return invocation().getFlash();
	}

	/**
	 * 获取在URI、请求查询串(即问号后的xx=yyy)中所带的参数值
	 * <p>
	 * URI中的参数需要通过在action方法中通过类似@ReqMapping(path="abc_${name}")进行声明，才可以获取name的参数<br>
	 * 对于id=3&name=5这样的参数如果绑定到一个bean中(比如@Param("user") User user)，那么Rc.param("user")将返回给bean对象
	 * <p>
	 * 如果这个参数在action方法中声明了参数，则返回该参数，即不再是String类型的。<br>
	 * 如果这个参数没有在action方法中声明，则返回的是普通String类型<br>
	 * 如果没有存在给定的参数，返回null
	 * <p>
	 * 
	 * @param name
	 * @return
	 */
	public static Object param(String name) {
		return invocation().getParameter(name);
	}
	
	/**
	 * 获取本次请求的 {@link Invocation}对象，在已经成功进入Controllers时有效
	 * 
	 * @return
	 */
	public static Invocation invocation() {
		return PrivateVar.getInvocation();
	}

	/**
	 * 获取控制器action方法普通参数的绑定结果
	 * 
	 * @return
	 */
	public static BindingResult getParameterBindingResult() {
		return getBindingResult(PrivateVar.getInvocation().getController());
	}

	/**
	 * 获取控制器action方法各个bean的绑定结果
	 * 
	 * @param bean
	 * @return
	 */
	public static BindingResult getBindingResult(Object bean) {
		Assert.notNull(bean);
		if (bean instanceof String) {
			return (BindingResult) model().get(BindingResult.MODEL_KEY_PREFIX + bean);
		} else {
			Invocation invocation = PrivateVar.getInvocation();
			if (invocation.getController() == bean) {
				return (BindingResult) model().get(
						BindingResult.MODEL_KEY_PREFIX + ParameterBindingResult.OBJECT_NAME);
			}
			Object[] params = invocation.getMethodParameters();
			for (int i = 0; i < params.length; i++) {
				if (bean.equals(params[i])) {
					return getBindingResult(invocation.getMethodParameterNames()[i]);
				}
			}
		}
		return null;
	}

}
